package com.smallstep.apilimit.ratelimiter;

import java.util.concurrent.TimeUnit;

/**
 * @author 李俊
 * @Description
 * @Date 2019/9/4 16:26
 */
public class RedisPerimits {
    /**
     * 最大存储令牌数
     */
     Long maxPermits;
    /**
     * 当前存储令牌数
     */
    Long storedPermits;
    /**
     * 添加令牌时间间隔
     */
    Long intervalMillis;
    /**
     * 下次请求可以获取令牌的起始时间，默认当前系统时间
     */
    Long nextFreeTicketMillis = System.currentTimeMillis();

    public RedisPerimits() {

    }
    /**
     *
     * @param maxPermits
     * @param storedPermits
     * @param intervalMillis
     * @param nextFreeTicketMillis
     */
    public RedisPerimits(Long maxPermits, Long storedPermits,
                         Long intervalMillis, Long nextFreeTicketMillis) {
        this.maxPermits = maxPermits;
        this.storedPermits = storedPermits;
        this.intervalMillis = intervalMillis;
        this.nextFreeTicketMillis = nextFreeTicketMillis;
    }

    /**
     * 构建Redis令牌数据模型
     * @param permitsPerSecond 每秒放入的令牌数
     * @param maxBurstSeconds maxPermits由此字段计算，最大存储maxBurstSeconds秒生成的令牌
     * @param nextFreeTicketMillis 下次请求可以获取令牌的起始时间，默认当前系统时间
     */
    public RedisPerimits(Double permitsPerSecond,Integer maxBurstSeconds,
                         Long nextFreeTicketMillis) {
        this( new Double(permitsPerSecond * maxBurstSeconds).longValue(),
                permitsPerSecond.longValue(),
                new Double(TimeUnit.SECONDS.toMillis(1) / permitsPerSecond).longValue(),
                nextFreeTicketMillis);
    }
    /**
     * 计算redis-key过期时长（秒）
     *
     * @return redis-key过期时长（秒）
     */
    public Long expires() {
        Long now = System.currentTimeMillis();
        return 2 * TimeUnit.MINUTES.toSeconds(1) +
                TimeUnit.MILLISECONDS.toSeconds(Math.max(nextFreeTicketMillis, now) - now);
    }


    /**
     * if nextFreeTicket is in the past, reSync to now
     * 若当前时间晚于nextFreeTicketMicros，则计算该段时间内可以生成多少令牌，将生成的令牌加入令牌桶中并更新数据
     *
     * @return 是否更新
     */
    Boolean reSync(Long now) {
        if (now > nextFreeTicketMillis) {
            storedPermits = Math.min(maxPermits,
                    storedPermits + (now - nextFreeTicketMillis) / intervalMillis);
            nextFreeTicketMillis = now;
            return true;
        }
        return false;
    }


    public Long getMaxPermits() {
        return maxPermits;
    }

    public void setMaxPermits(Long maxPermits) {
        this.maxPermits = maxPermits;
    }

    public Long getStoredPermits() {
        return storedPermits;
    }

    public void setStoredPermits(Long storedPermits) {
        this.storedPermits = storedPermits;
    }

    public Long getIntervalMillis() {
        return intervalMillis;
    }

    public void setIntervalMillis(Long intervalMillis) {
        this.intervalMillis = intervalMillis;
    }

    public Long getNextFreeTicketMillis() {
        return nextFreeTicketMillis;
    }

    public void setNextFreeTicketMillis(Long nextFreeTicketMillis) {
        this.nextFreeTicketMillis = nextFreeTicketMillis;
    }
}
